Websydian v6.1 online documentationOnline documentation - WebsydianExpress v3.5

BinaryPageGenerator for WebsydianExpress

Overview

The BinaryPageGenerator pattern is used when you want to let the user download a file from the application to his PC/browser.

The file can either be a static file or it can be a file that you have generated at an earlier step in the processing of the user request.

The BinaryPageGenerator reads the file and streams the content unaltered to the browser after setting HTTP-headers that describe the content.

This document will describe how to implement BinaryPageGenerators and how to use them in a WebsydianExpress site.

BinaryPageGenerator Interface

DocumentName

The file name of the file to download.

DocumentPath

The full path to the folder containing the file to download. This must end with a path separator ("\" or "/", depending on the variant).

The DocumentPath and DocumentName are concatenated to create the location of the file to download.

The DocumentName will be used as the name of the file in the default content type header set by the system.

HTTP-Headers

The developer must specify the HTTP-headers that is sent before the file-content.

The HTTP-headers describe the content of the file and how the browser should handle the file.

The pattern does not have any way to determine the content of the file that is being handled, so the developer must ensure that the correct headers are sent.

The unaltered BinaryPageGenerator sends three headers (described below). You can add additional headers if needed.

Content-Type

The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient. This header makes it possible for the browser to determine which application it can use to handle the file-content (based on the users system preferences/browser settings).

Content-disposition

The Content-disposition header is used by the browser to determine whether the file content should be handled as content that should be shown right away as part of the web site being loaded (Content-disposition:inline) or whether file should be handled as a downloaded file that the user can choose to either save or display (Content-disposition:attachment).

For Content-disposition:attachment, you can also specify the filename the user will be informed about - this results in the format:

Content-disposition:attachement;filename="myfilename".

Filename

As far as can be determined, no current browser versions use this header.

Specifying headers

The headers are specified in the subroutine "Send headers" in the BinaryPageGenerator.

If you need to add further headers it is vital that it is done in this subroutine, as the headers must be set after the connection is initialized, but before the file content is streamed.

You can add additional headers by setting the values for the local fields Local<HeaderName> and Local<HeaderValue> followed by a Go Sub write document line statement.

If you want to change the values of the three headers the standard BinaryPageGenerator already specifies, you must undefine the entire subroutine, copy the content of the subroutine and set the headers to the values described above.

Media-types

To be able to send the correct information in the Content-type header, it is necessary to know the media type that corresponds to the file content.

Note that MS-Office changed the document formats in version 2007. This means that each MS-Office program has two separate formats, each with it's own media type.

For example, Word documents have a version (.doc), which corresponds to the media type application/msword and a version (.docx), which corresponds to the media type: application/vnd.openxmlformats-officedocument.wordprocessingml.document.

The content of the file determines which media type you must specify.

Note that the content will be shown by the application the user has specified for the media type you specify in the headers. If the user has selected a program that can't open the media, an error will occur. If the user hasn't selected an application for the specified media type, he will be prompted to select one.

Browser Specific Handling

Different browsers handle the Content-Type and Content-disposition in different ways.

When using the BinaryPageGenerator, it is even more important than usual to test all combinations of browsers, browser versions, and file content that you want to support. Even for a specific browser the handling of a specifc content type can be different in different versions of the browser.

Example BinaryPageGenerator implementations

PDF - show in browser

Headers

The media type for pdf is: application/pdf

When the default behavior is to show the file in the browser, the Content-disposition should be set to inline.

Implementing

 

Define the BinaryPageGenerator by specifying the triple:

Source Object Verb Target Object
MyBinaryPageGenerator is a FNC BinaryPageGenerator

Create two messages that will be used to create the Content-type and Content-disposition header values

Source Object Verb Target Object
MyBinaryPageGenerator message MSG ContentType
MyBinaryPageGenerator message MSG ContentDisposition

Specify the following literal values:

MyBinaryPageGenerator.ContentType: application/pdf

MyBinaryPageGenerator.ContentDisposition: inline

 

In the Post Point "Start Send Headers", enter the following code:

 

+If Variant: WsyBase/DWA - Windows

+Else

    Call WsyBase/WpkaSetResponseCharset

Map with:

WsyBase<ConnectionPointer>

ResponseCharset<*BINARY>

Set Document<TypeInfo> = <TypeInfo.Specific>

Set Document<DocumentAction> = <DocumentAction.Open>

Set Local<HeaderName> = <HeaderName.Content-type>

Format Message Message: MyBinaryPageGenerator.ContentType, Local<HeaderValue>

Go Sub Write document line

Set Local<HeaderName> = <HeaderName.Content-disposition>

Format Message Message: MyBinaryPageGenerator.ContentDisposition, Local<HeaderValue>

Go Sub Write document line

+++Undefine Field: FIELDS/+Subroutine

You should note that the last statement ensures that the normal handling performed by the subroutine is not generated.

The call to WpkaSetResponseCharset is is normally made in the subroutine itself, but as this handling has been undefined, you have to enter the call in the post point (as shown above).

The rest of the code just sets the values for the Content-Type and Content-disposition headers.

Reference to example model

In the example model, the function BinaryPageGenerators.BinaryPageGeneratorPDFInline is an example implementation of this case.

Notes

It seems the Chrome browser can't load the pdf inline if the target is a frame. This means that there can very well be problems when loading a pdf in a frame in WebsydianExpress. You can avoid this issue by specifying target=_top in the HTML form for the event that shows the pdf.

PDF - as file attachment

Headers

The media type for pdf is: application/pdf.

When the default behavior is to save the file, the Content-disposition should be set to attachment - and a suggested filename should be specified.

Implementing

 

Define the BinaryPageGenerator by specifying the triple:

Source Object Verb Target Object
MyBinaryPageGenerator is a FNC WSYBASE/BinaryPageGenerator

Create two messages that will be used to create the Content-type and Content-disposition header values

Source Object Verb Target Object
MyBinaryPageGenerator message MSG ContentType
MyBinaryPageGenerator message MSG ContentDisposition
MyBinaryPageGenerator.ContentDisposition parameter FLD WSYBASE/DocumentName

Specify the following literal values:

MyBinaryPageGenerator.ContentType: application/pdf

MyBinaryPageGenerator.ContentDisposition: attachment; filename="&(1:)"

 

In the Post Point "Start Send Headers enter the following code:

 

+If Variant: WsyBase/DWA - Windows

+Else

    Call WsyBase/WpkaSetResponseCharset

Map with:

WsyBase<ConnectionPointer>

ResponseCharset<*BINARY>

Set Document<TypeInfo> = <TypeInfo.Specific>

Set Document<DocumentAction> = <DocumentAction.Open>

Set Local<HeaderName> = <HeaderName.Content-type>

Format Message Message: MyBinaryPageGenerator.ContentType, Local<HeaderValue>

Go Sub Write document line

Set Local<HeaderName> = <HeaderName.Content-disposition>

Format Message Message: MyBinaryPageGenerator.ContentDisposition, Local<HeaderValue>

Map with:

Document<DocumentName>

Go Sub Write document line

+++Undefine Field: FIELDS/+Subroutine

You should note that the last statement ensures that the normal handling performed by the subroutine will not be generated.

The call to WpkaSetResponseCharset is is normally made in the subroutine itself, but as this handling has been undefined, you have to enter the call in the post point (as shown above).

The rest of the code just sets the values for the Content-Type and Content-disposition headers.

If you want to suggest a filename to the user that is not the same as the one used in your file system, specify a field containing this value for the parameter to the format message statement for the ContentDisposition message.

Reference to example model

In the example model, the function BinaryPageGenerators.BinaryPageGeneratorPDFAttachment is an example implementation of this case.

Notes

In Chrome 10, this will show a standard file/folder selection dialog. This only allows the user to save the file.

In Firefox 4, this shows a dialog, where the user can select to either save or open the file. The default will be the last selection the user has made.

In Internet Explorer 8, this shows a dialog where the user can select to either save or open the file. The default seems to be to cancel the download.

Force download

In Firefox 4, when you choose Content-disposition attachment, the user will be shown a dialog, where he can choose to either save the file or open it.

It seems that the selection will be the one the user has selected the last time - not necessarily the save option.

In some cases, the application should always propose that the file should be saved. This seems to be possible using a slightly different combination of Content-Type and Content-disposition.

In this case, you specify the Content-Type: application/force-download and the Content-disposition: attachment;filename="myfilename.pdf".

This instructs the application that it should always propose that the file should be saved.

Internet Explorer 8 and Chrome 10 seems to be indifferent to whether the Content-Type is set to application/pdf or to application/force-download.

Note that application/force-download is not a standard media type, so you can't be certain that all browsers will be able to handle it. Therefore, you should only use this if you need to ensure that the save option is selected when the user uses Firefox.

Implementing

 

Define the BinaryPageGenerator by specifying the triple:

Source Object Verb Target Object
MyBinaryPageGenerator is a FNC WSYBASE/BinaryPageGenerator

Create two messages that will be used to create the Content-type and Content-disposition header values

Source Object Verb Target Object
MyBinaryPageGenerator message MSG ContentType
MyBinaryPageGenerator message MSG ContentDisposition
MyBinaryPageGenerator.ContentDisposition parameter FLD WSYBASE/DocumentName

Specify the following literal values:

MyBinaryPageGenerator.ContentType: application/force-download

MyBinaryPageGenerator.ContentDisposition: attachment; filename="&(1:)"

 

In the Post Point "Start Send Headers enter the following code:

 

+If Variant: WsyBase/DWA - Windows

+Else

    Call WsyBase/WpkaSetResponseCharset

Map with:

WsyBase<ConnectionPointer>

ResponseCharset<*BINARY>

Set Document<TypeInfo> = <TypeInfo.Specific>

Set Document<DocumentAction> = <DocumentAction.Open>

Set Local<HeaderName> = <HeaderName.Content-type>

Format Message Message: MyBinaryPageGenerator.ContentType, Local<HeaderValue>

Go Sub Write document line

Set Local<HeaderName> = <HeaderName.Content-disposition>

Format Message Message: MyBinaryPageGenerator.ContentDisposition, Local<HeaderValue>

Map with:

Document<DocumentName>

Go Sub Write document line

+++Undefine Field: FIELDS/+Subroutine

You should note that the last statement ensures that the normal handling performed by the subroutine will not be generated.

The call to WpkaSetResponseCharset is is normally made in the subroutine itself, but as this handling has been undefined, you have to enter the call in the post point.

The rest of the code just sets the values for the Content-Type and Content-disposition headers.

If you want to suggest a filename to the user that is not the same as the one used in your file system, specify a field containing this value for the parameter to the format message statement for the ContentDisposition message.

Reference to example model

In the example model, the function BinaryPageGenerators.BinaryPageGeneratorPDFForceDownload is an example implementation of this case.

Notes

In Chrome 10, this will show a standard file/folder selection dialog. This only allows the user to save the file.

In Firefox 4, this shows a dialog, where the user can select to either save or open the file. The default will save.

In Internet Explorer 8, this shows a dialog where the user can select to either save or open the file.

Calling BinaryPageGenerators Examples

When a button is pressed

In many cases, you want to initiate the download when a button or a link on a page is pressed.

When this is the case, you must start by having a normal PageGeneratorForProcess, that includes or comprises an EventHandlerForProcess that calls the BinaryPageGenerator.

It is important to note that after calling the BinaryPageGenerator, you should not call a normal PageGenerator. The BinaryPageGenerator creates a complete Http-response - so calling another PageGenerator afterwards will result in the browser receiving two Http-responses for one request - which will lead to unpredictable results.

Reference to example model

The function BinaryPageGenerators.ExampleProcessEntryPoint is a ProcessEntryPoint, which calls a PageGenerator that includes three EventHandlers. These three EventHandlers calls the BinaryPageGenerators described above.

In these EventHandlers, the path and the name of the file to download is just specified by Plex message objects (using the Format Message statement).

In some cases, you would want to use values from the WebInput variable of the EventHandler to either identify a static file that should be downloaded or to generate the file that should be downloaded.

Deploying the example in the WebsydianExpress site

To deploy this as an example business process in your own site, Generate and build the functions scoped by the BinaryPageGenerators entity. Deploy the resulting objects to your site as you would for any of your own custom business processes. Remember to also run the BinaryPageGenerators.ExampleProcessEntryPoint.StandardPageGenerator._DocumentTemplateGenerator function to generate the HTML template for the standard page.

Using the administration interface, create a business process content loader. Specify BPGPE as the implementation name.

Add this business process to a suitable place in your site structure and add the relevant roles so that you can access the business process.

When a deep link is pressed

Another option is to have an external link that enables the user to download the file. This could. e.g. be if you have a product sheet that should be directly downloadable without the user having to enter the WebsydianExpress site.

You can do this by creating a menu item for the file to download - and then let the user have a deep link to this menu item. However, this would mean that you would have to have a menu item for each document you want to make available for download - which is rarely a good idea.

Instead the example will show you how you can create one menu item in the site structure, the user will then be able to specify which file he wants to download by adding the filename as a parameter on the deep link.

Reference to the example model

The function BinaryPageGenerators.DirectLinkShowFile is a deep link that calls the BinaryPageGenerators.BinaryPageGeneratorPDFInline BinaryPageGenerator after extracting the filename from the request.

You add the request parameter by adding the field you want to access to the WebInput and to the ExcludeFromSign variables.

After doing so, generate and build the BinaryPageGenerators.DirectLinkShowFile and BinaryPageGenerators.BinaryPageGeneratorPDFInline functions.

Deploy the objects to your WebsydianExpress installation.

In the administration interface, create a business process content loader - specify the implementation name BPDIRLNK.

Add this as a menu item in the site structure.

Create an alias named DirectLink (Site StructureAliases). In the site structure specify this alias for the menu item.

After doing so, you would be able to call the

http://[MyHost]:[MyPort]/express30/site/demosite?WSLOAD=DirectLink&WSYD015=Example3.pdf&WSCONTEXT=Y&WSPARMLIST=WSYD015

WSLOAD specifies what menu item to load.

WSYD015 is the document name and specifies the file to load.

WSCONTEXT specifies that the menu item should be loaded in the frame specified by the menu item created above. If you specify N, the document will be loaded in a top frame.

WSPARMLIST is used by the WebsydianExpress runtime to resolve the document name parameter.

Notes to the example

In the example, the ProcessEntryPoint allows the user to specify the filename. In many cases, you would not want to use the real filename, but instead let the user specify an alias that you would then resolve to the filename in the ProcessEntryPoint.

It is important to note that the filename is not secured by a signature. This means that the user can specify any filename he can imagine.

One effect of this is that if you let users download files that are specific to them using this method (e.g. a pdf of their last order confirmation), you must ensure that the user only can access his own files. One way could be to let the ProcessEntryPoint determine the folder by the user name.

The BinaryPageGenerator assumes that it should load a pdf. If this functionality should be able to handle multiple media types, the ProcessEntryPoint should also specify the media type as a parameter on the call to the BinaryPageGenerator - or the BinaryPageGenerator could resolve the media type based on the file extension.

In the example, the ProcessEntryPoint determines the file folder instead of letting the user specify the folder in the URL. Generally it is not recommended to let a user determine the folder to download from - at least not as a direct reference to the folder system.